﻿using System;
using System.Drawing;
using System.Windows.Forms;

using OpenTK;
using OpenTK.Input;
using OpenTK.Graphics.OpenGL;

using MosCraft.Utilities;

namespace MosCraft
{
    public class Camera
    {
        private Vector3d position;
        private Vector3d lookDirection;
        private Vector3d up;
        private Vector3d right;
        private float bobbing;
        private double x, y, z;

        /** Rotations of the camera, in radians */
        private double yaw;
        private double pitch;

        /** Angle of the scene, taken by this camera. Defined in degrees */
        private float fovy;

        private ViewFrustum viewFrustum;

        public Camera()
        {
            viewFrustum = new ViewFrustum();
            position = new Vector3d();
            lookDirection = new Vector3d();
            up = new Vector3d(0, .5D, 0);
            right = Vector3d.Zero;
        }

        public Camera(Camera cam)
            : this()
        {
            x = cam.x;
            y = cam.y;
            z = cam.z;
            pitch = cam.pitch;
            yaw = cam.yaw;
            fovy = cam.fovy;
            viewFrustum = new ViewFrustum();
        }

        public ViewFrustum getViewFrustum()
        {
            return viewFrustum;
        }

        public void lookThrough(float viewingDistance)
        {
            // Change to projection matrix.
            GL.MatrixMode(MatrixMode.Projection);
            GL.LoadIdentity();

            // Perspective.
            float widthHeightRatio = (float)MosCraft.clientWidth / (float)MosCraft.clientHight;
            Matrix4 m_prespective = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(fovy), widthHeightRatio, 0.1f, viewingDistance);
            GL.LoadMatrix(ref m_prespective);   
       
            // Change back to model view matrix.
            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadIdentity();

            //Matrix4d lookat =  Matrix4d.LookAt(x, y + Math.Abs(bobbing) * 3.5D, z, 
            //    x + lookDirection.X, y + lookDirection.Y + Math.Abs(bobbing) * 3.5D, z + lookDirection.Z,
            //    up.X + right.X, up.Y + right.Y, up.Z + right.Z);

            Matrix4d lookat =  Matrix4d.LookAt(x, y + Math.Abs(bobbing) * 3.5D, z, 
                x + lookDirection.X, y + lookDirection.Y + Math.Abs(bobbing) * 3.5D, z + lookDirection.Z,
                0, 1, 0);

            GL.MatrixMode(MatrixMode.Modelview);
            GL.LoadMatrix(ref lookat);

            viewFrustum.updateFrustum();
        }

        public void setPosition(double x, double y, double z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
            this.position = new Vector3d(x, y, z);
        }

        public void setRotation(double pitch, double yaw)
        {
            this.yaw = yaw;
            this.pitch = pitch;
            this.lookDirection = new Vector3d(Math.Cos(yaw), Math.Tan(pitch), -Math.Sin(yaw));
            this.right = Vector3d.Cross(lookDirection, up);         
        }

        public void setFov(float fovy)
        {
            this.fovy = fovy;
        }

        public float getFov()
        {
            return fovy;
        }

        public double getPitch()
        {
            return pitch;
        }

        public double getYaw()
        {
            return yaw;
        }

        public double getX()
        {
            return x;
        }

        public double getY()
        {
            return y;
        }

        public double getZ()
        {
            return z;
        }

        public Vector3d getPosition()
        {
            return position;
        }

        public Vector3d getLookDirection()
        {
            return lookDirection;
        }

    }
}
